package top.rish.converter.factory;

import top.rish.converter.base.DictEntity;
import top.rish.converter.handler.ConvertHandler;
import top.rish.converter.info.EntityWrap;
import top.rish.converter.info.FieldWrap;
import top.rish.converter.info.MetaMap;
import top.rish.converter.utils.ObjKit;
import top.rish.converter.utils.RefKit;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class ConvertMetropolitan {

    public static final Map<Field, List<ConvertHandler<? extends Annotation>>>
            FIELD_AND_HANDLER = new ConcurrentHashMap<>();

    public static final Map<Field , FieldWrap>
            FIELD_FIELD_WRAP_MAP = new ConcurrentHashMap<>();

    public static final Map<Class<?>,Set<Field>> ENTITY_CLASS_AND_FIELDS = new ConcurrentHashMap<>();
    public static final Map<Class<? extends Annotation>,ConvertHandler<? extends Annotation>>
            ANNO_AND_HANDLER = new ConcurrentHashMap<>();

    public static final List<ConvertHandler<? extends Annotation>> HANDLERS = new ArrayList<>();



    public static void registerHandler(ConvertHandler<? extends Annotation> handler){
        if(ObjKit.isEmpty(handler)   ){
            return;
        }
        Class<? extends Annotation> ac = handler.getAnnonationClass();
        if(ac==null  ){
            return;
        }
        HANDLERS.add(handler);
        if(!ANNO_AND_HANDLER.containsKey(ac)){
            ANNO_AND_HANDLER.put(ac,handler);
        }
    }

    public static Set<Field> getAllDictFields(Object o){
        if(o instanceof DictEntity ) {
            return RefKit.getAllFields(o);
        }
        return null;
    }
    public static void putEntityAndFields(Class<?> oc,Set<Field> fields){
        if(!ENTITY_CLASS_AND_FIELDS.containsKey(oc)){
            ENTITY_CLASS_AND_FIELDS.put(oc,fields);
        }else{
            Set<Field> fieldList = ENTITY_CLASS_AND_FIELDS.get(oc);
            fieldList.addAll(fields);
        }
    }

    public static void registerFields(Object o){
        if(ObjKit.isEmpty(o)){
            return;
        }
        if(ObjKit.isTypeCollect(o)){
            if(ObjKit.isArray(o)){
                Object[] ol = ((Object[]) o);
                for (Object oo : ol) {
                    ConvertMetropolitan.registerFields(oo);
                }
            }else if(ObjKit.isIterable(o)){
                Iterable<?> iterable = ((Iterable<?>)o);
                iterable.forEach(ConvertMetropolitan::registerFields);
            }else if(ObjKit.isIterator(o)){
                Iterator<?> iterator = ((Iterator<?>)o);
                while (iterator.hasNext()){
                    ConvertMetropolitan.registerFields(iterator.next());
                }
            }
        } else if(ObjKit.isMap(o)){
            Map<?,?> map = ((Map<?,?>)o);
            map.keySet().forEach(ConvertMetropolitan::registerFields);
            map.values().forEach(ConvertMetropolitan::registerFields);
        } else {
            Class<?> oc = o.getClass();
            Set<Field> dictFields = getAllDictFields(o);
            if(ObjKit.isEmpty(dictFields)){
                return;
            }
            if(ObjKit.isEmpty(HANDLERS)){
                return;
            }
            EntityWrap entityWrap =  EntityWrap.of(o);
            for (Field f : dictFields) {
                FieldWrap fw = FieldWrap.of(f);
                Annotation[] annotations = f.getAnnotations();
                if(ObjKit.isEmpty(annotations)){
                    continue;
                }
                FIELD_FIELD_WRAP_MAP.put(f,fw);
                fw.setEntityWrap(entityWrap);
                List<ConvertHandler<? extends Annotation>> handlerList =
                        Arrays.stream(annotations).map(a ->

                                        ANNO_AND_HANDLER.get(a.annotationType()))
                                .collect(Collectors.toList());
                if(ObjKit.isNotEmpty(handlerList)){
                    FIELD_AND_HANDLER.put(f,handlerList);
                }
            }
            putEntityAndFields(oc,dictFields);
        }
    }



    public static Object convert(Object o) {
        if(ObjKit.isEmpty(HANDLERS)){
            return o;
        }
        if(ObjKit.isTypeCollect(o)){
            List<Object> objList;
            if(ObjKit.isArray(o)){
                Object[] ol = ((Object[]) o);
                objList = new ArrayList<>(Arrays.asList(ol));
            }else if(ObjKit.isIterable(o)){
                objList = new ArrayList<>();
                Iterable<?> iterable = ((Iterable<?>)o);
                iterable.forEach(objList::add);
            }else if(ObjKit.isIterator(o)){
                objList = new ArrayList<>();
                Iterator<?> iterator = ((Iterator<?>)o);
                while (iterator.hasNext()){
                    objList.add(iterator.next());
                }
            }else{
                return o;
            }
            return  objList.stream()
                    .map(ConvertMetropolitan::convert)
                    .collect(Collectors.toList());
        } else if(ObjKit.isMap(o)){
            Map<?,?> map = ((Map<?,?>)o);
            Map<Object,Object> om = new HashMap<>();
            map.forEach((k,v)->{
                om.put(ConvertMetropolitan.convert(k),ConvertMetropolitan.convert(v));
            });
            return  om;
        } else if(o instanceof DictEntity){
            Set<Field> fs = RefKit.getAllFields(o);
            MetaMap mateMap = MetaMap.ofEntity(o);
            if(ObjKit.isNotEmpty(fs)){
                for (Field field : fs) {
                    Object value = RefKit.getFieldValue(o, field);
                    mateMap.put(field.getName(),ConvertMetropolitan.convert(value));
                    List<ConvertHandler<? extends Annotation>> handlers = FIELD_AND_HANDLER.get(field);
                    FieldWrap fieldWrap = FIELD_FIELD_WRAP_MAP.get(field);
                    if (ObjKit.isNotEmpty(handlers)) {
                        for (ConvertHandler<? extends Annotation> handler : handlers) {
                            Class<Annotation> aClass = RefKit.getGenericClass(handler.getClass(), 0);
                            assert aClass != null;
                            Annotation annotation = field.getAnnotation(aClass);
                            handler.doConvert(mateMap,(DictEntity) o ,value, fieldWrap,
                                    annotation);
                        }
                    }
                }
            }
            return  mateMap;
        }else{
            return o;
        }
    }
}
